package br.com.joocebox.controller;

import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import br.com.joocebox.model.Customer;
import br.com.joocebox.model.CustomerService;
import br.com.joocebox.model.Destination;
import br.com.joocebox.model.FamilyBond;
import br.com.joocebox.model.History;
import br.com.joocebox.model.Passenger;
import br.com.joocebox.model.SaleType;
import br.com.joocebox.model.ServiceItem;
import br.com.joocebox.model.views.VwOpenService;
import br.com.joocebox.service.CustomerFacade;
import br.com.joocebox.service.CustomerServiceFacade;
import br.com.joocebox.service.DashboardFacade;
import br.com.joocebox.service.ServiceFacade;
import br.com.joocebox.service.ServiceItemFacade;

@Controller
@Transactional(propagation = Propagation.REQUIRED)
@RequestMapping("/auth")
public class CustomerController {

	final static Logger logger = LoggerFactory
			.getLogger(CustomerController.class);

	@Autowired
	private ServiceFacade serviceFacade;
	
	@Autowired
	private CustomerFacade customerFacade;
	
	@Autowired
	private CustomerServiceFacade customerServiceFacade;
	
	@Autowired
	private ServiceItemFacade serviceItemFacade;
	
	@Autowired
	private DashboardFacade dashboardFacade;

	private Set<CustomerService> customerServiceList = new HashSet<CustomerService>();
	
	private Set<Passenger> passengerList = new HashSet<Passenger>();

	private List<ServiceItem> serviceItemList = new ArrayList<ServiceItem>();
	
	private List<History> history;
	

	@RequestMapping("/service")
	public ModelAndView getMenuService(Model model) {

		initializeComponents(model);
		model.addAttribute("requestedDestination", new ServiceItem());
		return new ModelAndView("service/newService", "customer",
				new Customer());
	}

	@RequestMapping("/serviceList")
	public ModelAndView getMenuServiceList(Model model) {

		List<VwOpenService> openServiceList = serviceFacade.getOpenServiceList();
		
		List<VwOpenService> auxCustomer = new ArrayList<VwOpenService>();
		
		for (VwOpenService vwOpenService : openServiceList) {
			
	        if ("SUBMITTED_BUDGET".equals(vwOpenService.getSaleType()) || "SEND_BUDGET".equals(vwOpenService.getSaleType())) {
	            auxCustomer.add(vwOpenService);
	            
	        }
			
		}

		ModelAndView mv = new ModelAndView("service/serviceList");
		mv.addObject("serviceListRegister", auxCustomer);
		mv.addObject("ListSize", auxCustomer.size());
		avgBudgets(model);
		getCountOfBudgets(model);
		
		return mv;

	}

	public void initializeComponents(Model model) {

		// Carrega os destinos no mutiple-select
		List<Destination> destinationList = dashboardFacade
				.getDestinationList();
		model.addAttribute("destinationList", destinationList);

		// Carrega o parentesco do passageiro
		Map<FamilyBond, String> map = new HashMap<FamilyBond, String>();
		for (FamilyBond family : Arrays.asList(FamilyBond.values())) {
			String bondName = family.getBond();
			map.put(family, bondName);
		}
		model.addAttribute("listOfBondNames", map);
		
		//Carrega os tipos de negociação
		Map<SaleType, String> saleTypeMap = new HashMap<SaleType, String>();
		for (SaleType sale : Arrays.asList(SaleType.values())) {
			String saleType = sale.getSaleType();
			saleTypeMap.put(sale, saleType);
		}
		
		model.addAttribute("listOfSaleTypes", saleTypeMap);

	}

    /**
     * Ação responsável por adicionar um novo atendimento
     * @param Customer
     * @param result
     * @param model
     * @return ModelAndView
     */
	@RequestMapping(value = "/addService", method = RequestMethod.POST)
	public ModelAndView processForm(@ModelAttribute("customer") @Valid Customer customer, BindingResult result, Model model) {
		if (customer.getBirthDate() == null) {
			customer.setBirthDate(customer.getBirthDate());
		}

		List<String> error = new ArrayList<String>();

		if (result.hasErrors()) {
			List<ObjectError> allErrors = result.getAllErrors();

			for (ObjectError objectError : allErrors) {
				String objectName = objectError.getDefaultMessage();
				error.add(objectName);
			}

			model.addAttribute("errors", error);
			model.addAttribute("validator", true);

			initializeComponents(model);
			return new ModelAndView("service/newService");

		} else {
			
			//Agrega uma lista de passageiros cadastrados a aquele cliente corrente
			customer.setPassenger(passengerList);
			
			//Persiste um novo Cliente
			customerFacade.saveCustomer(customer);
			
            //Inicia um objeto do tipo CustomerService para abrir um atendimento.
            CustomerService customerService = new CustomerService();
			
            //Atualiza a data de abertura do serviço
            customerService.setDate(new Date());
            
            //Verifica se existe algum atendimento marcado como "Orçamento", e marca como aberto e atualiza a data do mesmo
			if(isOpenService()){
				customerService.setSituation(Boolean.TRUE);			
			}else{
				customerService.setSituation(Boolean.FALSE);
			}
			
            //Adiciona o atendimento em uma lista
            customerServiceList.add(customerService);
            
            //Seta uma lista de Atendimentos
            customer.setCustomerService(customerServiceList);
                       
            //Cria um objeto com o Historico
            history = new ArrayList<History>();
            
            //Salva um objeto do tipo ServiceItem
            for (ServiceItem si : serviceItemList) {
            	
            	//Adiciona um "Registo" no historico
            	history.add(new History("[ " + si.getSaleType() + " ]" + " Destino: " + si.getDestination().getDtName() + " Na data de: " + si.getArrivalDate().toString()));
            	
            	customerService.setHistory(history);
                si.setCustomerService(customerService);
            }
            
            serviceItemFacade.saveServiceItem(serviceItemList);
			
			
			return new ModelAndView(new RedirectView("serviceList"));
		}

	}
	
    /**
     * Ação responsável por apresentar o formulário de edição do serviço
     * @param id
     * @param model
     * @return destinationNegotiated
     */
    @RequestMapping(value = "/editService/{id}", method = RequestMethod.GET)
    public String editService(@PathVariable Long id, Model model) {
    	
    	Customer customerId = customerFacade.getCustomerId(id);
    	getHistoryListCustomerId(model, customerId);
    	
    	List<String> destinationName = new ArrayList<String>();
    	
    	for(CustomerService cs : customerId.getCustomerService()){
    		for(ServiceItem si : cs.getServiceItem()){
    			String dtName = si.getDestination().getDtName();
    			destinationName.add(dtName);
    			
    		}
    	}
    	model.addAttribute("destinationNegotiated", destinationName);
        model.addAttribute("serviceModify", customerId);
        return "service/editService";
    }

	public void getHistoryListCustomerId(Model model, Customer customerId) {
		for(CustomerService cs : customerId.getCustomerService()){
    		List<History> historyMain = cs.getHistory();
    		model.addAttribute("historyList", historyMain);
    	}
	}
	
    /**
     * Ação responsável pela somatorio de um contador de orçamentos
     * @param model
     */
	public void getCountOfBudgets(Model model){
		List<ServiceItem> serviceItemList = serviceItemFacade.getAllServiceItems();
		
		int send_budget_var = 0;
		int submitted_budget_var = 0;
		
		for (ServiceItem serviceItem : serviceItemList) {
			if(serviceItem.getSaleType() == SaleType.SEND_BUDGET){
				send_budget_var ++;
				
			}else if(serviceItem.getSaleType() == SaleType.SUBMITTED_BUDGET){
				submitted_budget_var ++;
			}
		}
		model.addAttribute("send_budget_div", send_budget_var);
		model.addAttribute("submitted_budget_div", submitted_budget_var);
	}

    /**
     * Ação responsável por verificar se existem destinos negociados como "Orçamento Enviado ou Enviar Orçamento"
     * @return Boolean
     */
	public Boolean isOpenService() {
		for (ServiceItem serviceItem : serviceItemList) {
			if(serviceItem.getSaleType() == SaleType.SEND_BUDGET || serviceItem.getSaleType() == SaleType.SUBMITTED_BUDGET){
				return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}

    /**
     * Ação responsável por calcular o valor total dos orçamentos enviados
     * @param model
     */
	public void avgBudgets(Model model) {
		List<ServiceItem> serviceItemList = serviceItemFacade.getAllServiceItems();
		
		Double amount = 0.00;
		
		for (ServiceItem si : serviceItemList) {
			if (si.getSaleType() == SaleType.SUBMITTED_BUDGET) {
				Double price = si.getValueNegotiated();
				amount += price;
			}
		}
		model.addAttribute("totalAmount", amount);
	}
	
    /**
     * Ação responsável por adicionar passageiros
     * @param id
     * @param result
     * @param model
     * @return Passenger
     */
	@RequestMapping(value = "/addPassenger", method = RequestMethod.POST)
	public @ResponseBody Passenger addPassenger(@Valid Passenger passenger, BindingResult result, Model model) {

		if (result.hasErrors()) {
			logger.warn("[Novo Atendimento] Validação de campos de adição de passageiros com falhas!");
			return null;
		} else {
			passengerList.add(passenger);
			initializeComponents(model);
			logger.info("[Novo Atendimento] Passageiro adicionado a lista...");
			return passenger;
		}

	}
	
    /**
     * Ação responsável por deletar um passageiro
     * @param id
     * @return ServiceItem
     */
	@RequestMapping(value="/deletePassenger/{id}", method=RequestMethod.DELETE)
	public @ResponseBody List<Passenger> deletePassagenger(@PathVariable Long id){
//		for (Passenger pas : passengerList) {
//			if (pas.get == id) {
//				serviceItemList.remove(sil);
//				logger.info("[Novo Atendimento] Destino excluido do atendimento...");
//				return serviceItemList;
//			}
//		}
//		return serviceItemList;
		return null;
	}
	
    /**
     * Ação responsável por deletar os destinos requisitados
     * @param id
     * @return ServiceItem
     */
	@RequestMapping(value="/deleteRequestedDestination/{id}", method=RequestMethod.DELETE)
	public @ResponseBody List<ServiceItem> deleteRequestedDestination(@PathVariable Long id){
		for (ServiceItem sil : serviceItemList) {
			if (sil.getDestination().getIdDestination() == id) {
				serviceItemList.remove(sil);
				logger.info("[Novo Atendimento] Destino excluido do atendimento...");
				return serviceItemList;
			}
		}
		return serviceItemList;
	}
	
    /**
     * Ação responsável por retornar o Destino Requisitado
     * @param id
     * @param model
     * @return ServiceItem
     */
    @RequestMapping(value = "/editRequestedDestination/{id}", method = RequestMethod.GET)
    public @ResponseBody ServiceItem editRequestedDestination(@PathVariable Long id, Model model) {
    	try {
            return getDestinationRequestedList(id);
		} catch (NullPointerException e) {
			e.printStackTrace();
		}
    	return getDestinationRequestedList(id);
    }
    
    
    private ServiceItem getDestinationRequestedList(Long id) {
		for (ServiceItem serviceItem : serviceItemList) {
			if(serviceItem.getDestination().getIdDestination() == id){
				return serviceItem;
			}
		}
		return null;
	}

	/**
     * Ação responsável por atualizar os dados enviados pelo formulário.
     * 
     * @param result
     * @param ServiceItem
     * @param model
     */
    @RequestMapping(value = "/updateDestinationRequested", method = RequestMethod.POST)
    @ResponseStatus(value=HttpStatus.OK)
    public void updateCategory(@RequestParam Long id,
			@RequestParam(value = "arrive", required = true) Date arrive,
            @RequestParam(value = "departure", required = true) Date departure,
            @RequestParam(value = "price", required = true) String price,
            @RequestParam(value = "saletype", required = true) SaleType saletype,
            @RequestParam(value = "destinationId", required = true) Long destinationId,
            @RequestParam(value = "observations", required = true) String observations,
            @RequestParam(value = "ckb", required = true) Boolean ckb,
            Model model) {

        if ((!"".equals(arrive) && !"".equals(departure) && !"".equals(price)
                && !"".equals(saletype) && !"".equals(destinationId))
                && (!arrive.before(departure))){
        	
        	for(ServiceItem si : serviceItemList){
        		if(id.equals(si.getDestination().getIdDestination())){
        			Locale locBrazil = new Locale("pt", "BR");
        			NumberFormat nf = NumberFormat.getInstance(locBrazil);
        			try {
        				Number parse = nf.parse(price);
        				
        				SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
        				String formatArrive = df.format(arrive);
        				String formatDeparture = df.format(departure);
        								
        				Destination destinationObj = dashboardFacade.getDestinationId(destinationId);
        								
        				si.setDestination(destinationObj);
        				si.setArrivalDate(formatDate(formatArrive));
        				si.setDepartureDate(formatDate(formatDeparture));
        				si.setNegociationObservations(observations);
        				si.setSaleType(saletype);
        				si.setValueNegotiated(parse.doubleValue());
        				si.setRequestedDestination(ckb);

        				serviceItemList.set(serviceItemList.indexOf(si), si);
                        			
        				logger.info("[Novo Atendimento] Destino atualizado com sucesso...");

        			} catch (ParseException e) {

        				e.printStackTrace();
        			}
        		}
        	}
        	
		}else{
			model.addAttribute("updateError", true);
		}

    }
    
    /**
     * Adiciona um novo destino requisitado
     * @return ServiceItem
     */
	@RequestMapping(value = "/addSelectedDestination", method = RequestMethod.POST)
	public @ResponseBody List<String> addSelectedDestination(
			@RequestParam(value = "arrive", required = true) Date arrive,
            @RequestParam(value = "departure", required = true) Date departure,
            @RequestParam(value = "price", required = true) String price,
            @RequestParam(value = "saletype", required = true) SaleType saletype,
            @RequestParam(value = "destinationId", required = true) Long destinationId,
            @RequestParam(value = "observations", required = true) String observations,
            @RequestParam(value = "ckb", required = true) Boolean ckb,
		    Model model) {
		
			List<String> strList = null;
		
			ServiceItem serviceItem = new ServiceItem();

        if ((!"".equals(arrive) && !"".equals(departure) && !"".equals(price)
                && !"".equals(saletype) && !"".equals(destinationId))
                && (!arrive.before(departure))){

			Locale locBrazil = new Locale("pt", "BR");
			NumberFormat nf = NumberFormat.getInstance(locBrazil);
			try {
				Number parse = nf.parse(price);			
				
				String arriveFormat = new SimpleDateFormat("dd/MM/yyyy").format(arrive);
				String departureFormat = new SimpleDateFormat("dd/MM/yyyy").format(departure);
								
				Destination destinationObj = dashboardFacade.getDestinationId(destinationId);
								
                serviceItem.setDestination(destinationObj);
                serviceItem.setArrivalDate(formatDate(arriveFormat));
                serviceItem.setDepartureDate(formatDate(departureFormat));
                serviceItem.setNegociationObservations(observations);
                serviceItem.setSaleType(saletype);
                serviceItem.setValueNegotiated(parse.doubleValue());
                serviceItem.setRequestedDestination(ckb);
                			
				logger.info("[Novo Atendimento] Destino negociado com sucesso...");

			} catch (ParseException e) {

				e.printStackTrace();
			}

			serviceItemList.add(serviceItem);
			
			String destinationName = serviceItem.getDestination().getDtName();
			
			String destinationIdStr =  String.valueOf(serviceItem.getDestination().getIdDestination());
			
			strList = new ArrayList<String>();
			
			strList.add(destinationName);
			strList.add(destinationIdStr);
			
			return strList;

		}
		logger.warn("[Novo Atendimento] Argumentos passados para adição de destino em falta ou data de partida superior a de chegada");
		return strList;

	}
	
	@InitBinder(value = "customer")
	public void bindingPreparationDate(WebDataBinder binder) {
		DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
		CustomDateEditor birthDateEditor = new CustomDateEditor(df, false);
		binder.registerCustomEditor(Date.class, birthDateEditor);
	}

	public Date formatDate(String date) {
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
		try {
			Date parse = sdf.parse(date);
			return parse;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
		
	}

}